﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="STSAdmAdapter.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   Allows SAF to be called from a custom
//   STSAdm extension.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.Saf.Adapters
{
    using System;
    using System.Collections.Specialized;
    using System.IO;
    using System.Text;

    using Collaboris.Saf.Engine;
    using Collaboris.Saf.Engine.Report;
    using Collaboris.Utils;

    using Microsoft.SharePoint.StsAdmin;

    /// <summary>
    /// Allows SAF to be called from a custom
    /// STSAdm extension.
    /// </summary>
    public sealed class STSAdmAdapter : AdapterBase, ISPStsadmCommand
    {
        #region Constants and Fields

        /// <summary>
        /// actions file.
        /// </summary>
        private string macroFile;

        /// <summary>
        /// current mode.
        /// </summary>
        private ProcessMode mode;


        private StringDictionary arguments;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the Adapter String for the
        /// current agent.
        /// </summary>
        /// <value>The current directory.</value>
        public override string AdapterName
        {
            get
            {
                return "STSAdm";
            }
        }

        /// <summary>
        /// Gets or sets Mode.
        /// </summary>
        /// <exception cref="NotImplementedException">
        /// </exception>
        public override ProcessMode Mode
        {
            get
            {
                return this.mode;
            }

            set
            {
                this.mode = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Called when the Adapter needs to add PlaceHolders
        /// from (its specific environment) to a SAF Macro.
        /// </summary>
        /// <param name="ctx">
        /// </param>
        protected override void AddProperties(SafContext ctx)
        {
            foreach (string key in arguments.Keys)
            {
                string val = arguments[key];
                ctx.ActionProperties.Add(key, val, true);
            }
        }

        /// <summary>
        /// Gets the help for the message.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The get help.
        /// </returns>
        public string GetHelp(string command)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "GetHelp");

            var sb = new StringBuilder();
            sb.AppendLine("stsadm.exe -o " + command.ToLower());
            sb.AppendLine(@"           -url <url for site (e.g. 'http://localhost')>");
            sb.AppendLine(@"           -macrofile <path to a macro of actions (e.g. 'c:\macro.xml')> ");
            sb.AppendLine(@"           -mode <specify whether to 'do' or 'undo' the macro> ");

            TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "GetHelp");

            return sb.ToString();
        }

        #endregion

        #region Implemented Interfaces

        #region ISPStsadmCommand

        /// <summary>
        /// Returns a Help Message on how to use the command.
        /// </summary>
        /// <param name="command">
        /// </param>
        /// <returns>
        /// a string containing helpinformation on the command
        /// </returns>
        public string GetHelpMessage(string command)
        {
            return this.GetHelp(command);
        }

        /// <summary>
        /// Locates the macro and runs the command containing
        /// the Actions.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <param name="keyValues">
        /// The key values.
        /// </param>
        /// <param name="output">
        /// The output.
        /// </param>
        /// <returns>
        /// The run.
        /// </returns>
        public int Run(string command, StringDictionary keyValues, out string output)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "Run");

            // Remove the starting and leading apostrophes.
            this.RemoveWrappingChars(ref keyValues);
            this.EnsureProps(keyValues);

            arguments = keyValues;

            TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "Run");
            return this.RunMacro(out output);
        }

        #endregion

        #endregion

        #region Helper Methods

        /// <summary>
        /// on get macro.
        /// </summary>
        /// <param name="ctx">
        /// The ctx.
        /// </param>
        /// <returns>
        /// An Macro initialised Macro
        /// </returns>
        protected override IMacro GetMacro(SafContext ctx)
        {
            try
            {
                TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "OnGetMacro");

                if (!Path.IsPathRooted(this.macroFile))
                {
                    TraceHelper.TraceVerbose(
                        TraceSwitch, "STSAdmAdapter", "The path to the 'macrofile' is not rooted - so rooting it.");

                    this.macroFile = Path.Combine(this.CurrentDirectory, this.macroFile);
                }

                if (!File.Exists(this.macroFile))
                {
                    throw new FileNotFoundException(
                        string.Format(
                            "The Action file '{0}' cannot be found, " + "please check the 'macrofile' parameter.", 
                            this.macroFile));
                }

                TraceHelper.TraceInfo(
                    TraceSwitch, 
                    "STSAdmAdapter", 
                    "Attempting to create the Macro from this file Path '{0}'", 
                    this.macroFile);

                IMacro newMacro = MacroEngine.CreateFromFile(this.macroFile, ctx);

                return newMacro;
            }
            finally
            {
                TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "OnGetMacro");
            }
        }

        /// <summary>
        /// Gets the name of the macro file on disk.
        /// </summary>
        /// <returns>
        /// The get macro name.
        /// </returns>
        protected override string GetMacroName()
        {
            return Path.GetFileName(this.macroFile);
        }

        /// <summary>
        /// Removes the wrapping chars.
        /// </summary>
        /// <param name="keyValues">
        /// The key values.
        /// </param>
        protected void RemoveWrappingChars(ref StringDictionary keyValues)
        {
            var newDictionary = new StringDictionary();
            TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "RemoveWrappingChars");
            foreach (string key in keyValues.Keys)
            {
                string val = keyValues[key];
                if (val.StartsWith("'") && val.EndsWith("'"))
                {
                    if (val == @"''")
                        newDictionary.Add(key, null);
                    else
                        newDictionary.Add(key, val.Substring(1, val.Length - 2));
                }
                //else if (val.StartsWith("") && val.EndsWith(""))
                //{
                //    if (val == @"")
                //        newDictionary.Add(key, null);
                //    else
                //        newDictionary.Add(key, val.Substring(1, val.Length - 2));
                //}
                else
                {

                    newDictionary.Add(key, keyValues[key]);
                }
            }

            keyValues = newDictionary;

            TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "RemoveWrappingChars");
        }

        /// <summary>
        /// Grabs Values from the parameters passed in, that ALL commands should pass.
        /// </summary>
        /// <param name="keyValues">
        /// The key values.
        /// </param>
        private void EnsureProps(StringDictionary keyValues)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "EnsureProps");
            if (!keyValues.ContainsKey("url"))
            {
                // throw new InvalidOperationException("Missing required argument: url");
                // if no url is passed then the Local Farm is assumed.
            }
            else
            {
                this.TargetUrl = keyValues["url"];
            }

            if (!keyValues.ContainsKey("macrofile"))
            {
                // left in for backward compatability
                if (!keyValues.ContainsKey("actionsfile"))
                {
                    throw new InvalidOperationException("Missing required argument: macrofile");
                }

                this.macroFile = keyValues["macrofile"];
            }
            else
            {
                this.macroFile = keyValues["macrofile"];
            }

            if (!keyValues.ContainsKey("mode"))
            {
                throw new InvalidOperationException("Missing required argument: mode");
            }

            this.mode = (ProcessMode)Enum.Parse(typeof(ProcessMode), keyValues["mode"], true);

            TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "EnsureProps");
        }

        /// <summary>
        /// Runs the macro.
        /// </summary>
        /// <param name="output">
        /// The output.
        /// </param>
        /// <returns>
        /// 0 for success, 1 for failure
        /// </returns>
        private int RunMacro(out string output)
        {
            try
            {
                TraceHelper.TraceMethodStart(TraceSwitch, "STSAdmAdapter", "RunMacro");

                // Calls the Do or the Undo on the Macro
                MacroReport report = this.ExecuteMacro();

                if (report != null)
                {
                    output = "Your macro returned a status of : '" + report.Status + "'";    
                }
                else
                {
                    output = "Your macro ran successfully.";
                }
            }
            catch (Exception ex)
            {
                TraceHelper.TraceException(TraceSwitch, "STSAdmAdapter", "RunMacro", ex);

                output = ex.ToString();
                return -1;
            }
            finally
            {
                TraceHelper.TraceMethodEnd(TraceSwitch, "STSAdmAdapter", "RunMacro");
            }

            return 1;
        }

        #endregion
    }
}